package org.bapoint.bawirelessprogressusercenter.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.annotation.PostConstruct;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Redis 多数据库配置（兼容 Spring Boot 3.0+）
 */
@Configuration
@Data
public class RedisConfig {

    @Value("${spring.data.redis.host}")
    private String host;

    @Value("${spring.data.redis.port}")
    private int port;

    @Value("${spring.data.redis.username}")
    private String username;

    @Value("${spring.data.redis.password}")
    private String password;

    @Value("${spring.data.redis.timeout}")
    private Integer timeout;

    @Value("${spring.redis.dbs}")
    private List<Integer> dbList;

    // 默认数据库
    private int defaultDB;

    // 缓存连接工厂（避免重复创建）
    private final Map<Integer, LettuceConnectionFactory> connectionFactoryMap = new ConcurrentHashMap<>();

    // 缓存 RedisTemplate 实例
    private static final Map<Integer, RedisTemplate<String, Object>> redisTemplateMap = new HashMap<>();

    /**
     * 初始化所有数据库的 RedisTemplate
     */
    @PostConstruct
    public void initRedisTemplate() {
        // 设置默认数据库（例如第13个）
        if (dbList != null && !dbList.isEmpty()) {
            this.defaultDB = dbList.get(12 % dbList.size()); // 防越界
        }

        for (Integer db : dbList) {
            redisTemplateMap.put(db, redisTemplate(db));
        }
    }

    /**
     * 获取指定数据库的连接工厂（复用）
     */
    private LettuceConnectionFactory redisConnection(int db) {
        return connectionFactoryMap.computeIfAbsent(db, key -> {
            RedisStandaloneConfiguration server = new RedisStandaloneConfiguration();
            server.setHostName(host);
            server.setPort(port);
            server.setUsername(username);
            server.setPassword(password);
            server.setDatabase(db);
            LettuceConnectionFactory factory = new LettuceConnectionFactory(server);
            factory.afterPropertiesSet(); // 初始化
            return factory;
        });
    }

    /**
     * 创建指定数据库的 RedisTemplate（Spring Boot 3.0 兼容版）
     */
    public RedisTemplate<String, Object> redisTemplate(int db) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnection(db));

        // === 配置 ObjectMapper（关键：安全 + 兼容）===
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.configure(JsonParser.Feature.IGNORE_UNDEFINED, true); // 忽略 null 字段
        om.registerModule(new JavaTimeModule()); // 支持 LocalDateTime

        // 安全的多态类型验证器（限制反序列化范围）
        PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
                .allowIfSubType("org.bapoint") // ✅ 仅允许你的项目包下的类（防止 RCE）
                .build();

        // 启用默认类型信息（替代 enableDefaultTyping）
        om.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL);

        // 使用更安全的 GenericJackson2JsonRedisSerializer
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(om);

        // 设置序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }

    /**
     * 根据数据库索引获取 RedisTemplate
     */
    public RedisTemplate<String, Object> getRedisTemplateByDb(int db) {
        RedisTemplate<String, Object> template = redisTemplateMap.get(db);
        if (template == null) {
            throw new IllegalArgumentException("RedisTemplate for db " + db + " not initialized. Check spring.redis.dbs config.");
        }
        return template;
    }

    /**
     * 使用默认数据库
     */
    public RedisTemplate<String, Object> getRedisTemplate() {
        return getRedisTemplateByDb(defaultDB);
    }
}